package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.pojo.RequestParam;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {

    @Autowired
    private RestHighLevelClient client;

    /**
     * 查询酒店列表
     *
     * @param param
     * @return
     */
    @Override
    public PageResult list(RequestParam param) {
        // 1.创建请求对象
        SearchRequest request = new SearchRequest("hotel");

        //构建boolQuery条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 2.设置请求参数(查询条件类型和查询结果类型)
        if (StringUtils.isBlank(param.getKey())) {
            //没有key,全查
            boolQuery.must(QueryBuilders.matchAllQuery());
        } else {
            boolQuery.must(QueryBuilders.matchQuery("all", param.getKey()));
        }

        //精确查询
        if (StringUtils.isNoneBlank(param.getBrand())) {
            boolQuery.filter(QueryBuilders.termQuery("brand", param.getBrand())); //组合了过滤条件：根据品牌精确查询
        }

        if (StringUtils.isNoneBlank(param.getCity())) {
            boolQuery.filter(QueryBuilders.termQuery("city", param.getCity())); //组合了过滤条件：根据城市精确查询
        }

        if (StringUtils.isNoneBlank(param.getStarName())) {
            boolQuery.filter(QueryBuilders.termQuery("starName", param.getStarName())); //组合了过滤条件：根据星级精确查询
        }

        if (param.getMinPrice() != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(param.getMinPrice())); //组合了过滤条件：根据价格范围查询
        }

        if (param.getMaxPrice() != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(param.getMaxPrice())); //组合了过滤条件：根据价格范围查询
        }


        //设置查询条件最终类型 : 算分函数查询(嵌套布尔查询)
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQuery,
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("isAD", true),
                                ScoreFunctionBuilders.weightFactorFunction(10)
                        )
                }
        );

        //添加请求条件
        request.source().query(functionScoreQueryBuilder);

        //设置查询结果类型
        Integer from = (param.getPage() - 1) * param.getSize();
        request.source()
                .from(from)
                .size(param.getSize());

        //对价格或者评价排序
        if (StringUtils.isNoneBlank(param.getSortBy()) && !param.getSortBy().equals("default")) {
            request.source().sort(param.getSortBy(), SortOrder.DESC);//默认降序
        }


        //对地理位置排序,根据我的定位由远及近排序
        if (StringUtils.isNoneBlank(param.getLocation())) {
            request.source().sort(SortBuilders.geoDistanceSort("location", new GeoPoint(param.getLocation()))
                    .order(SortOrder.ASC)
                    .unit(DistanceUnit.KILOMETERS)
            );
        }


        try {
            // 3.发起请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            // 4.解析请求
            SearchHits hits = response.getHits();
            long total = hits.getTotalHits().value;
            List<HotelDoc> hotelDocs = new ArrayList<>();
            SearchHit[] searchHits = hits.getHits();
            if (searchHits != null && searchHits.length > 0) {
                for (SearchHit hit : searchHits) {
                    String hotelJsonStr = hit.getSourceAsString();

                    //添加距离
                    HotelDoc hotelDoc = JSON.parseObject(hotelJsonStr, HotelDoc.class);
                    Object[] sortValues = hit.getSortValues();
                    if (sortValues != null && sortValues.length > 0) {
                        hotelDoc.setDistance(sortValues[0]);
                    }
                    hotelDocs.add(hotelDoc);
                }
            }
            // 5.构建返回结果
            return new PageResult(total, hotelDocs);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}
